﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CarSimulate
{
    internal class TrafficLine
    {
        public PointF StartPoint;
        public PointF EndPoint;
        public int StartPointKey;
        public int EndPointKey;


        private double m_lineLenght = -1;
        public double Length
        {
            get
            {
                if(m_lineLenght>=0) { return m_lineLenght; }
                double dx = EndPoint.X - StartPoint.X;
                double dy = EndPoint.Y - StartPoint.Y;
                m_lineLenght= Math.Sqrt(dx * dx + dy * dy);
                return m_lineLenght;
            }
        }

        public int LinePId { get; internal set; }

        //要么是下一个路段
        public List<TrafficLine> NextLines=new List<TrafficLine>();

        // 获取从当前点沿线段移动指定距离后的新点
        internal void GetNextPoint(CarInLineStruct carPosition, double distance = 10.0)
        {
            // 计算线段方向向量
            double dx = EndPoint.X - StartPoint.X;
            double dy = EndPoint.Y - StartPoint.Y;

            // 如果线段长度为0，返回终点
            if (Length == 0)
            {
                carPosition.CurrentPoint = EndPoint;
                carPosition.NeedFindNextPath = true;
                carPosition.LeftDistanceToGo = distance;
                return;
            }


            //这种情况下需要做投影找位置
            // 计算单位方向向量
            double unitX = dx / Length;
            double unitY = dy / Length;

            // 计算新点的坐标
            double newX = carPosition.CurrentPoint.X + unitX * distance;
            double newY = carPosition.CurrentPoint.Y + unitY * distance;
            if (carPosition.AbsInLine == false)
            {
                var q = GetProjectionPoint(new PointF((float)newX, (float)newY));
                newX = q.X;
                newY = q.Y;
            }

            // 确保新点不会超过终点
            double progress = ((newX - StartPoint.X) * dx + (newY - StartPoint.Y) * dy) / (dx * dx + dy * dy);
            if (progress > 1.0)
            {
                carPosition.CurrentPoint = EndPoint;
                carPosition.NeedFindNextPath = true;
                carPosition.LeftDistanceToGo = Math.Sqrt((newX - EndPoint.X) * (newX - EndPoint.X) 
                    + (newY - EndPoint.Y) * (newY - EndPoint.Y));
                return;
            }
            carPosition.CurrentLineRank = progress;
            carPosition.NeedFindNextPath = false;
            carPosition.CurrentPoint = new PointF((float)newX, (float)newY);
            carPosition.LeftDistanceToGo = 0;
        }

        /// <summary>
        /// 计算点到线段的投影点
        /// </summary>
        /// <param name="point">要计算投影的点</param>
        /// <returns>在线段上的投影点，或者线段的端点</returns>
        internal PointF GetProjectionPoint(PointF point)
        {
            // 计算线段向量
            double lineVecX = EndPoint.X - StartPoint.X;
            double lineVecY = EndPoint.Y - StartPoint.Y;

            // 计算点到起点的向量
            double pointVecX = point.X - StartPoint.X;
            double pointVecY = point.Y - StartPoint.Y;

            // 计算线段长度的平方
            double lineLengthSquared = lineVecX * lineVecX + lineVecY * lineVecY;

            // 如果线段长度为0，直接返回起点
            if (lineLengthSquared == 0)
                return StartPoint;

            // 计算投影比例 (点积 / 线段长度的平方)
            double t = (pointVecX * lineVecX + pointVecY * lineVecY) / lineLengthSquared;

            // 限制t在[0,1]范围内，确保投影点在线段上
            t = Math.Max(0, Math.Min(1, t));

            // 计算投影点坐标
            double projX = StartPoint.X + t * lineVecX;
            double projY = StartPoint.Y + t * lineVecY;

            return new PointF((float)projX, (float)projY);
        }

        /// <summary>
        /// 计算点在线段上的归一化投影距离 (rmd)
        /// </summary>
        /// <param name="point">要计算的点</param>
        /// <param name="segmentStart">线段起点</param>
        /// <param name="segmentEnd">线段终点</param>
        /// <returns>归一化投影距离 (0=起点, 1=终点)</returns>
        public double CalculateRmd(PointF point)
        {
            PointF segmentStart = this.StartPoint;
            PointF segmentEnd = this.EndPoint;
            // 计算线段向量
            double segmentVecX = segmentEnd.X - segmentStart.X;
            double segmentVecY = segmentEnd.Y - segmentStart.Y;

            // 如果线段长度为0，返回0（视为起点）
            if (segmentVecX == 0 && segmentVecY == 0)
                return 0;

            // 计算点到起点的向量
            double pointVecX = point.X - segmentStart.X;
            double pointVecY = point.Y - segmentStart.Y;

            // 计算点在线段上的投影长度
            double dotProduct = pointVecX * segmentVecX + pointVecY * segmentVecY;
            double segmentLengthSquared = segmentVecX * segmentVecX + segmentVecY * segmentVecY;

            // 计算归一化投影距离 (rmd)
            double rmd = dotProduct / segmentLengthSquared;

            // 限制在[0,1]范围内
            return Math.Max(0, Math.Min(1, rmd));
        }
    }
}
